/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package helloscala.practise.chapter7

import helloscala.practise.ChapterEntry

object Chapter7Entry extends ChapterEntry {
  def runTests: Unit = {
    testDetailLinerization
  }

  def testDetailLinerization = {
    class C1 {def m = List("C1")}
    trait T1 extends C1 {override def m = "T1" :: super.m }
    trait T2 extends C1 {override def m = "T2" :: super.m }
    trait T3 extends C1 {override def m = "T3" :: super.m }
    class C2A extends T2 {override def m = "C2A" :: super.m }

    class C2 extends C2A with T1 with T2 with T3 {
      override def m = { "C2" :: super.m }
    }

    def calcLinearization(obj: C1, name: String) = {
      val lin = obj.m ::: List("ScalaObject", "AnyRef", "Any")
      println(name + ":  " + lin)
    }

    calcLinearization(new C2, "C2 ")
    println("")
    calcLinearization(new T3 {}, "T3 ")
    calcLinearization(new T2 {}, "T2 ")
    calcLinearization(new T1 {}, "T1 ")
    calcLinearization(new C2A, "C2A")
    calcLinearization(new C1, "C1 ")
  }

  def testLinearization = {
    class C1 {def m = List("C1")}

    trait T1 extends C1 {override def m = "T1" :: super.m}
    trait T2 extends C1 {override def m = "T2" :: super.m}
    trait T3 extends C1 {override def m = "T3" :: super.m}
    class C2 extends T1 with T2 with T3 {override def m = "C2" :: super.m}

    val c2 = new C2
    println(c2.m reverse) //reverse is exactly the order the c2 constructed

    println(c2.m) //result: List(C2, T3, T2, T1, C1),
                  //or in actual: List(C2, T3, T2, T1, C1, ScalaObject, AnyRef, Any)
  }

  def testReturnEither = {
    def exception2Left[T](f: => T): Either[java.lang.Throwable, T] = try {
      Right(f)
    } catch {
      case ex:Throwable => Left(ex)
    }

    def throw4Odds(i: Int): Int = i%2 match {
      case 0 => i
      case _ => throw new RuntimeException(i + " is odd!")
    }

    for (i <- 0 to 3) {
      exception2Left[Int](throw4Odds(i)) match {
        case Right(f) => println(f)
        case Left(ex) => println(ex.toString)
        case _ => println("Unknown Result")
      }
    }
  }

  def testSealedClass = {

    //since all the possibilities are already known, "case _ => ..." is no
    //longer needed for matching. Note here if any one of the case statement
    //missing, say comment out the case "Cameron", runtime exception will happen
    def handleEmployee(employee: Employee) = employee match {
      case Jeff(sal) => println("Jeff: " + sal)
      case Winnie(sal) => println("Winnie: " + sal)
      case Cameron(sal) => println("Cameron: " + sal)
    }

    val family = List(Jeff(500.0), Winnie(600.0), Cameron(700.0))
    family foreach {person => handleEmployee(person)}
  }

  def testSingleton = {
    class C1(val id: Int) {
      def this(id: Int, name: String) = this(id)
    }

    object O1 { //object cannot have constructor param list like: (val id: Int)
      //def this(id: Int) = this() //nor the auxiliary constructor allowed
    }
  }

  def testWays2CreateTuple = {
    //the following 4 ways produce the same result -- an instance of Tuple2
    val t1 = (5, "Jeff")
    val t2 = Pair(5, "Jeff")
    val t3 = Tuple2(5, "Jeff")
    val t4 = 5 -> "Jeff"

    println(t1 == t2)
    println(t1 == t3)
    println(t1 == t4)
  }
}
